
//_________________________________Copyright (c) 2009,Mapabc Co.,Ltd_____________________________
//                                       All rights reserved.
//
//   文件名称：androidfile.cpp
//   文件标识：见配置管理计划书
//   描    述：android文件操作
//
//   当前版本：1.0
//   作    者：张小刚
//   完成日期：2022.02.08
//_______________________________________________________________________________________________

#include "osapi.h"

#ifdef ANDROID

#include "osandroidapi.h"
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>

extern void JavaWorkPath(char *cPath);

static std::string g_moduleFileName = "";
static std::string g_exePath = "";
static std::string g_modulePath = "";
static AAssetManager *g_assetManager = nullptr;

/**
 * @brief 设置可执行程序的全路径名称（由JAVA层获取正确目录后
 *        通过JNI调用此函数进行路径赋值）
 * @param fileName 全路径名称（编码：UTF-8）
 */
void osSetModuleFileName(const char *fileName)
{
	g_moduleFileName = fileName;
}

/**
 * @brief 设置可执行程序所在路径（由JAVA层获取正确目录后
 *        通过JNI调用此函数进行路径赋值）
 * @param cPath  路径（编码：UTF-8）
 */
void osSetExePath(const char *path)
{
	g_exePath = path;
}

/**
 * @brief 设置存放资源的目录,默认在windows、Linux下获得可执行程序所在的上一个目录,
 *        在symbian和iphone平台下指定可读写的目录即可(在osapi实现osGetModulePath)
 *        （由JAVA层获取正确目录后通过JNI调用此函数进行路径赋值）
 * @param path 路径（编码：UTF-8）
 */
void osSetModulePath(const char *path)
{
	g_modulePath = path;
}

/**
 * @brief 设置安卓系统下asserts目录管理器（此函数由JAVA层通过JNI调用）
 * @param assetManager  asserts目录管理器
 */
void osSetAssetManager(AAssetManager *assetManager)
{
	g_assetManager = assetManager;
}

/**
 * @brief 创建目录
 * @param cPathName 目录名称（编码：UTF-8）
 * @return  0      创建成功
 * @return -1      创建失败
 */
int32_t osCreateDirectory(const char *cPathName)
{
	if (nullptr == cPathName)
	{
		return -1;
	}
	return mkdir(cPathName, S_IRWXU);
}

/**
 * @brief 创建目录
 * @param cPathName 目录名称（编码：UTF-16）
 * @return  0      创建成功
 * @return -1      创建失败
 */
int32_t osCreateDirectory(const char16_t *cPathName)
{
	if (nullptr == cPathName)
	{
		return -1;
	}
	char u8PathName[256] = {0};
	if (osConvertToUTF8(cPathName, u8PathName, 256) <= 0)
	{
		return -1;
	}
	return mkdir(u8PathName, S_IRWXU);
}

/**
 * @brief 创建目录
 * @param cPathName 目录名称（编码：UTF-32）
 * @return  0      创建成功
 * @return -1      创建失败
 */
int32_t osCreateDirectory(const char32_t *cPathName)
{
	if (nullptr == cPathName)
	{
		return -1;
	}
	char u8PathName[256] = {0};
	if (osConvertToUTF8(cPathName, u8PathName, 256) <= 0)
	{
		return -1;
	}
	return mkdir(u8PathName, S_IRWXU);
}

/**
 * @brief 获取可执行程序的全路径名称（包含可执行程序名称）
 * @param fileName  存储可执行程序全路径名称（编码：UTF-8）
 * @param size      存储区域大小
 * @return 0        失败
 * @return !0       返回复制到fileName的实际字符数量
 */
uint32_t osGetModuleFileName(char *fileName, const uint32_t size)
{
	if (nullptr == fileName || 0 == size)
	{
		return 0;
	}

	if (g_moduleFileName.empty())
	{
		return 0;
	}

	uint32_t u64size = g_moduleFileName.size();
	u64size = u64size < size ? u64size : size;
	if (size == u64size)
	{
		u64size = size - 1;
	}

	for (uint32_t i = 0; i < u64size; ++i)
	{
		fileName[i] = g_moduleFileName.at(i);
	}
	fileName[u64size] = '\0';

	return u64size;
}

/**
 * @brief 获取可执行程序的全路径名称（包含可执行程序名称）
 * @param fileName  存储可执行程序全路径名称（编码: UTF-16）
 * @param size      存储区域大小
 * @return 0        失败
 * @return !0       返回复制到fileName的实际字符数量
 */
uint32_t osGetModuleFileName(char16_t *fileName, const uint32_t size)
{
	if (nullptr == fileName || 0 == size)
	{
		return 0;
	}

	if (g_moduleFileName.empty())
	{
		return 0;
	}

	return osConvertToUTF16(g_moduleFileName.c_str(), fileName, size);
}

/**
 * @brief 获取可执行程序的全路径名称（包含可执行程序名称）
 * @param fileName  存储可执行程序全路径名称（编码：UTF-32）
 * @param size      存储区域大小
 * @return 0        失败
 * @return !0       返回复制到fileName的实际字符数量
 */
uint32_t osGetModuleFileName(char32_t *fileName, const uint32_t size)
{
	if (nullptr == fileName || 0 == size)
	{
		return 0;
	}

	if (g_moduleFileName.empty())
	{
		return 0;
	}

	return osConvertToUTF32(g_moduleFileName.c_str(), fileName, size);
}

/**
 * @brief 获取指定目录的上一层
 * @param cPath       指定目录（编码：UTF-8）
 * @param cLastPath   上一层目录（编码：UTF-8）
 * @param size        存储上一层目录空间尺寸
 * @return  0         失败
 * @return  !0        返回复制到cLastPath的实际字符数量
 */
static uint32_t osGetLastDirectory(const char *cPath, char *cLastPath, const uint32_t size)
{
	if (nullptr == cPath || nullptr == cLastPath || 0 == size)
	{
		return 0;
	}
	memset(cLastPath, 0, size * sizeof(char));

	int32_t lPostion = -1;
	const int32_t len = osstrlen(cPath);
	for (int32_t i = 0; i < len; i++)
	{
		if ('\\' == cPath[i] || '/' == cPath[i])
		{
			lPostion = i;
		}
	}

	if (lPostion < 0 || static_cast<uint32_t>(lPostion) >= size)
	{
		return 0;
	}

	for (int32_t i = 0; i < lPostion; i++)
	{
		cLastPath[i] = cPath[i];
	}

	return static_cast<uint32_t>(lPostion);
}

/**
 * @brief 获取指定目录的上一层
 * @param cPath       指定目录（编码：UTF-16）
 * @param cLastPath   上一层目录（编码：UTF-16）
 * @param size        存储上一层目录空间尺寸
 * @return  0         失败
 * @return  !0        返回复制到cLastPath的实际字符数量
 */
static uint32_t osGetLastDirectory(const char16_t *cPath, char16_t *cLastPath, const uint32_t size)
{
	if (nullptr == cPath || nullptr == cLastPath || 0 == size)
	{
		return 0;
	}
	memset(cLastPath, 0, size * sizeof(char16_t));

	int32_t lPostion = -1;
	const int32_t len = osstrlen(cPath);
	for (int32_t i = 0; i < len; i++)
	{
		if (u'\\' == cPath[i] || u'/' == cPath[i])
		{
			lPostion = i;
		}
	}

	if (lPostion < 0 || static_cast<uint32_t>(lPostion) >= size)
	{
		return 0;
	}

	for (int32_t i = 0; i < lPostion; i++)
	{
		cLastPath[i] = cPath[i];
	}

	return static_cast<uint32_t>(lPostion);
}

/**
 * @brief 获取指定目录的上一层
 * @param cPath       指定目录（编码：UTF-32）
 * @param cLastPath   上一层目录（编码：UTF-32）
 * @param size        存储上一层目录空间尺寸
 * @return  0         失败
 * @return  !0        返回复制到cLastPath的实际字符数量
 */
static uint32_t osGetLastDirectory(const char32_t *cPath, char32_t *cLastPath, const uint32_t size)
{
	if (nullptr == cPath || nullptr == cLastPath || 0 == size)
	{
		return 0;
	}
	memset(cLastPath, 0, size * sizeof(char32_t));

	int32_t lPostion = -1;
	const int32_t len = osstrlen(cPath);
	for (int32_t i = 0; i < len; i++)
	{
		if (U'\\' == cPath[i] || U'/' == cPath[i])
		{
			lPostion = i;
		}
	}

	if (lPostion < 0 || static_cast<uint32_t>(lPostion) >= size)
	{
		return 0;
	}

	for (int32_t i = 0; i < lPostion; i++)
	{
		cLastPath[i] = cPath[i];
	}

	return static_cast<uint32_t>(lPostion);
}

/**
 * @brief 获取可执行程序的全路径
 * @param cPath    存储可执行程序全路径（编码：UTF-8）
 * @param size     存储区域大小
 * @return 0       失败
 * @return !0      返回复制到cPath的实际字符数量
 */
uint32_t osGetExePath(char *cPath, const uint32_t size)
{
	if (nullptr == cPath || 0 == size)
	{
		return 0;
	}

	if (g_exePath.empty())
	{
		return 0;
	}

	uint32_t u64size = g_exePath.size();
	u64size = u64size < size ? u64size : size;
	if (size == u64size)
	{
		u64size = size - 1;
	}

	for (uint32_t i = 0; i < u64size; ++i)
	{
		cPath[i] = g_exePath.at(i);
	}
	cPath[u64size] = '\0';

	return u64size;
}

/**
 * @brief 获取可执行程序的全路径
 * @param cPath    存储可执行程序全路径（编码：UTF-16）
 * @param size     存储区域大小
 * @return 0       失败
 * @return !0      返回复制到cPath的实际字符数量
 */
uint32_t osGetExePath(char16_t *cPath, const uint32_t size)
{
	if (nullptr == cPath || 0 == size)
	{
		return 0;
	}

	if (g_exePath.empty())
	{
		return 0;
	}

	return osConvertToUTF16(g_exePath.c_str(), cPath, size);
}

/**
 * @brief 获取可执行程序的全路径
 * @param cPath    存储可执行程序全路径（编码：UTF-32）
 * @param size     存储区域大小
 * @return 0       失败
 * @return !0      返回复制到cPath的实际字符数量
 */
uint32_t osGetExePath(char32_t *cPath, const uint32_t size)
{
	if (nullptr == cPath || 0 == size)
	{
		return 0;
	}

	if (g_exePath.empty())
	{
		return 0;
	}

	return osConvertToUTF32(g_exePath.c_str(), cPath, size);
}

/**
 * @brief 获得存放资源的目录,默认在windows、Linux下获得可执行程序所在的上一个目录,
 *        在symbian和iphone平台下指定可读写的目录即可(在osapi实现osGetModulePath)
 * @param cPath    存储资源路径（编码：UTF-8）
 * @param size     存储区域大小
 * @return 0       失败
 * @return !0      返回复制到cPath的实际字符数量
 */
uint32_t osGetModulePath(char *cPath, const uint32_t size)
{
	if (nullptr == cPath || 0 == size)
	{
		return 0;
	}

	if (g_modulePath.empty())
	{
		return 0;
	}

	uint32_t u64size = g_modulePath.size();
	u64size = u64size < size ? u64size : size;
	if (size == u64size)
	{
		u64size = size - 1;
	}

	for (uint32_t i = 0; i < u64size; ++i)
	{
		cPath[i] = g_modulePath.at(i);
	}
	cPath[u64size] = '\0';

	return u64size;
}

/**
 * @brief 获得存放资源的目录,默认在windows、Linux下获得可执行程序所在的上一个目录,
 *        在symbian和iphone平台下指定可读写的目录即可(在osapi实现osGetModulePath)
 * @param cPath    存储资源路径（编码：UTF-16）
 * @param size     存储区域大小
 * @return 0       失败
 * @return !0      返回复制到cPath的实际字符数量
 */
uint32_t osGetModulePath(char16_t *cPath, const uint32_t size)
{
	if (nullptr == cPath || 0 == size)
	{
		return 0;
	}

	if (g_modulePath.empty())
	{
		return 0;
	}

	return osConvertToUTF16(g_modulePath.c_str(), cPath, size);
}

/**
 * @brief 获得存放资源的目录,默认在windows、Linux下获得可执行程序所在的上一个目录,
 *        在symbian和iphone平台下指定可读写的目录即可(在osapi实现osGetModulePath)
 * @param cPath    存储资源路径（编码：UTF-32）
 * @param size     存储区域大小
 * @return 0       失败
 * @return !0      返回复制到cPath的实际字符数量
 */
uint32_t osGetModulePath(char32_t *cPath, const uint32_t size)
{
	if (nullptr == cPath || 0 == size)
	{
		return 0;
	}

	if (g_modulePath.empty())
	{
		return 0;
	}

	return osConvertToUTF32(g_modulePath.c_str(), cPath, size);
}

/**
 * @brief 打开文件
 * @param   cFileName    文件名称（编码：UTF-8）
 * @param   bReadOnly    只读标志
 * @param   bCreateNew   新建标志
 * @return  nullptr     失败
 * @return !nullptr    文件句柄
 * @note   如果当前为安卓系统，并且期望访问assets目录下文件，则需要将
 *         cFileName设置为如下格式"assets:目录"
 * @note   当文件处于安卓系统下的assets目录中时，只读，其它权限将被忽略
 */
void *osOpenFile(const char *cFileName, const bool bReadOnly, const bool bCreateNew)
{
	if (nullptr == cFileName)
	{
		return nullptr;
	}

	if (0 == osstrncmp(cFileName, "assets:", 7))
	{
		if (nullptr == g_assetManager)
		{
			return nullptr;
		}

		char assetsFileName[256] = {0x00};
		osstrcpy(assetsFileName, cFileName + 7);
		if (osstrlen(assetsFileName) <= 0)
		{
			return nullptr;
		}

		OsFileHandle *pFileHandle = (OsFileHandle *)malloc(sizeof(OsFileHandle));
		if (nullptr == pFileHandle)
		{
			return nullptr;
		}
		memset(pFileHandle, 0, sizeof(OsFileHandle));
		pFileHandle->m_pFileHandle = (void *)AAssetManager_open(g_assetManager, assetsFileName, AASSET_MODE_UNKNOWN);
		if (nullptr == pFileHandle->m_pFileHandle)
		{
			free(pFileHandle);
			return nullptr;
		}
		pFileHandle->m_bAssets = false;

		return (void *)pFileHandle;
	}
	else
	{
		int32_t *pFp = (int32_t *)malloc(sizeof(int32_t));
		if (nullptr == pFp)
		{
			return nullptr;
		}
		memset(pFp, 0, sizeof(int32_t));

		OsFileHandle *pFileHandle = (OsFileHandle *)malloc(sizeof(OsFileHandle));
		if (nullptr == pFileHandle)
		{
			free(pFp);
			return nullptr;
		}
		memset(pFileHandle, 0, sizeof(OsFileHandle));

		if (bReadOnly)
		{
			*pFp = open(cFileName, O_RDONLY);
		}
		else
		{
			if (bCreateNew)
			{
				*pFp = open(cFileName, O_CREAT | O_RDWR, S_IXUSR | S_IRUSR | S_IWUSR);
			}
			else
			{
				*pFp = open(cFileName, O_RDWR);
			}
		}

		if (-1 == *pFp)
		{
			free(pFp);
			free(pFileHandle);
			return nullptr;
		}
		else
		{
			pFileHandle->m_pFileHandle = (void *)pFp;
			pFileHandle->m_bAssets = false;
			return (void *)pFileHandle;
		}
	}
}

/**
 * @brief 打开文件
 * @param   cFileName    文件名称（编码：UTF-16）
 * @param   bReadOnly    只读标志
 * @param   bCreateNew   新建标志
 * @return  nullptr     失败
 * @return !nullptr    文件句柄
 * @note   如果当前为安卓系统，并且期望访问assets目录下文件，则需要将
 *         cFileName设置为如下格式"assets:目录"
 * @note   当文件处于安卓系统下的assets目录中时，只读，其它权限将被忽略
 */
void *osOpenFile(const char16_t *cFileName, const bool bReadOnly, const bool bCreateNew)
{
	if (nullptr == cFileName)
	{
		return nullptr;
	}

	char u8FileName[256] = {0x00};
	if (osConvertToUTF8(cFileName, u8FileName, 256) <= 0)
	{
		return nullptr;
	}

	return osOpenFile(u8FileName, bReadOnly, bCreateNew);
}

/**
 * @brief 打开文件
 * @param   cFileName    文件名称（编码：UTF-32）
 * @param   bReadOnly    只读标志
 * @param   bCreateNew   新建标志
 * @return  nullptr     失败
 * @return !nullptr    文件句柄
 * @note   如果当前为安卓系统，并且期望访问assets目录下文件，则需要将
 *         cFileName设置为如下格式"assets:目录"
 * @note   当文件处于安卓系统下的assets目录中时，只读，其它权限将被忽略
 */
void *osOpenFile(const char32_t *cFileName, const bool bReadOnly, const bool bCreateNew)
{
	if (nullptr == cFileName)
	{
		return nullptr;
	}

	char u8FileName[256] = {0x00};
	if (osConvertToUTF8(cFileName, u8FileName, 256) <= 0)
	{
		return nullptr;
	}

	return osOpenFile(u8FileName, bReadOnly, bCreateNew);
}

/**
 * @brief 关闭指定文件
 * @param hCom      文件句柄
 */
void osCloseFile(void **hCom)
{
	if (nullptr != *hCom)
	{
		OsFileHandle *pFileHandle = (OsFileHandle *)(*hCom);
		if (nullptr == pFileHandle->m_pFileHandle)
		{
			free(pFileHandle);
			*hCom = nullptr;
			return;
		}
		else
		{
			if (pFileHandle->m_bAssets)
			{
				AAsset_close((AAsset *)pFileHandle->m_pFileHandle);
				free(pFileHandle);
				*hCom = nullptr;
			}
			else
			{
				int32_t *pFp = (int32_t *)pFileHandle->m_pFileHandle;
				close(*pFp);
				free(pFileHandle->m_pFileHandle);
				free(pFileHandle);
				*hCom = nullptr;
			}
		}
	}
}

/**
 * @brief 检测指定目录是否存在
 * @param cPath 指定目录（编码：UTF-8）
 * @return true  目录存在
 * @return false 目录不存在
 */
bool osIsPathExist(const char *cPath)
{
	if (nullptr == cPath)
	{
		return false;
	}

	struct stat dirBuf;
	int ret = stat(cPath, &dirBuf);
	if (-1 != ret && (S_IFDIR & dirBuf.st_mode))
	{
		return true;
	}

	return false;
}

/**
 * @brief 检测指定目录是否存在
 * @param cPath 指定目录（编码：UTF-16）
 * @return true  目录存在
 * @return false 目录不存在
 */
bool osIsPathExist(const char16_t *cPath)
{
	if (nullptr == cPath)
	{
		return false;
	}

	char u8Path[256] = {0x00};
	if (osConvertToUTF8(cPath, u8Path, 256) <= 0)
	{
		return false;
	}

	return osIsPathExist(u8Path);
}

/**
 * @brief 检测指定目录是否存在
 * @param cPath 指定目录（编码：UTF-32）
 * @return true  目录存在
 * @return false 目录不存在
 */
bool osIsPathExist(const char32_t *cPath)
{
	if (nullptr == cPath)
	{
		return false;
	}

	char u8Path[256] = {0x00};
	if (osConvertToUTF8(cPath, u8Path, 256) <= 0)
	{
		return false;
	}

	return osIsPathExist(u8Path);
}

/**
 * @brief 读取文件
 * @param hFile                      文件句柄
 * @param lpBuffer                   数据缓冲区
 * @param nNumberOfBytesToRead       数据缓冲区字节数
 * @param lpNumberOfBytesRead        实际读取的字节数
 * @return true                      文件读取成功
 * @return false                     文件读取失败
 */
bool osReadFile(void *hFile, void *lpBuffer, const uint32_t nNumberOfBytesToRead, uint32_t *lpNumberOfBytesRead)
{
	if (nullptr == hFile || nullptr == lpBuffer || 0 == nNumberOfBytesToRead)
	{
		return false;
	}

	OsFileHandle *pFileHandle = (OsFileHandle *)hFile;
	if (nullptr == pFileHandle->m_pFileHandle)
	{
		return false;
	}

	if (pFileHandle->m_bAssets)
	{
		const int32_t ret = AAsset_read((AAsset *)pFileHandle->m_pFileHandle, lpBuffer, nNumberOfBytesToRead);
		if (ret < 0)
		{
			return false;
		}
		else
		{
			if (nullptr != lpNumberOfBytesRead)
			{
				*lpNumberOfBytesRead = ret;
			}
			return true;
		}
	}
	else
	{
		int32_t *pFp = (int32_t *)pFileHandle->m_pFileHandle;
		const int32_t ret = read(*pFp, lpBuffer, nNumberOfBytesToRead);
		if (ret < 0)
		{
			return false;
		}
		else
		{
			if (nullptr != lpNumberOfBytesRead)
			{
				*lpNumberOfBytesRead = ret;
			}
			return true;
		}
	}
}

/**
 * @brief 写文件
 * @param hFile                     文件句柄
 * @param lpBuffer                  数据缓冲区
 * @param nNumberOfBytesToWrite     数据字节数
 * @param lpNumberOfBytesWritten    实际写入字节数
 * @return true                     写入成功
 * @return false                    写入失败
 * @note  安卓assets目录中的文件不支持写入操作
 */
bool osWriteFile(void *hFile, void *lpBuffer, const uint32_t nNumberOfBytesToWrite, uint32_t *lpNumberOfBytesWritten)
{
	if (nullptr == hFile || nullptr == lpBuffer || 0 == nNumberOfBytesToWrite)
	{
		return false;
	}

	OsFileHandle *pFileHandle = (OsFileHandle *)hFile;
	if (nullptr == pFileHandle->m_pFileHandle)
	{
		return false;
	}

	int32_t *pFp = (int32_t *)pFileHandle->m_pFileHandle;
	const int32_t ret = write(*pFp, lpBuffer, nNumberOfBytesToWrite);
	if (ret > 0)
	{
		if (nullptr != lpNumberOfBytesWritten)
		{
			*lpNumberOfBytesWritten = ret;
		}

		return true;
	}

	return false;
}

/**
 * @brief   打开指定文件
 * @param   filename     文件名称（编码： UTF-8）
 * @param   mode         打开模式（r w a b t +）
 * @return  nullptr     打开失败
 * @return  !nullptr    文件句柄
 * @note    当文件处于 assets 目录中时，cFileName 应该以 assets: 前缀开始
 *          例如： asset:/log/log.log
 * @note    当文件处于安卓系统下的assets目录中时，只读，其它权限将被忽略
 */
void *osFopen(const char *filename, const char *mode)
{
	if (nullptr == filename || nullptr == mode)
	{
		return nullptr;
	}

	if (0 == osstrncmp(filename, "assets:", 7))
	{
		if (nullptr == g_assetManager)
		{
			return nullptr;
		}

		char assetsFileName[256] = {0x00};
		osstrcpy(assetsFileName, filename + 7);
		if (osstrlen(assetsFileName) <= 0)
		{
			return nullptr;
		}

		OsFileHandle *pFileHandle = (OsFileHandle *)malloc(sizeof(OsFileHandle));
		if (nullptr == pFileHandle)
		{
			return nullptr;
		}
		memset(pFileHandle, 0, sizeof(OsFileHandle));

		pFileHandle->m_pFileHandle = (void *)AAssetManager_open(g_assetManager, assetsFileName, AASSET_MODE_UNKNOWN);
		if (nullptr == pFileHandle->m_pFileHandle)
		{
			free(pFileHandle);
			return nullptr;
		}
		pFileHandle->m_bAssets = true;

		return (void *)pFileHandle;
	}
	else
	{
		// // we need to open the file as commit mode because
		// // otherwise the fflush will not work correctly
		// char newmode[8];
		// if (osstrlen(mode) > 6)
		// 	return NULL;
		// osstrcpy(newmode, mode);
		// osstrcat(newmode, "c");

		OsFileHandle *pFileHandle = (OsFileHandle *)malloc(sizeof(OsFileHandle));
		if (nullptr == pFileHandle)
		{
			return nullptr;
		}
		memset(pFileHandle, 0, sizeof(OsFileHandle));

		pFileHandle->m_pFileHandle = (void *)fopen(filename, mode);
		if (nullptr == pFileHandle->m_pFileHandle)
		{
			free(pFileHandle);
			return nullptr;
		}
		pFileHandle->m_bAssets = false;

		return (void *)pFileHandle;
	}
}

/**
 * @brief 打开指定文件
 * @param filename     文件名称（编码： UTF-16）
 * @param mode         打开模式（r w a b t +）
 * @return nullptr     打开失败
 * @return !nullptr    文件句柄
 * @note    当文件处于 assets 目录中时，cFileName 应该以 assets: 前缀开始
 *          例如： asset:/log/log.log
 * @note   当文件处于安卓系统下的assets目录中时，只读，其它权限将被忽略
 */
void *osFopen(const char16_t *filename, const char16_t *mode)
{
	if (nullptr == filename || nullptr == mode)
	{
		return nullptr;
	}

	char u8FileName[256] = {0x00};
	if (osConvertToUTF8(filename, u8FileName, 256) <= 0)
	{
		return nullptr;
	}
	char u8Mode[20] = {0x00};
	if (osConvertToUTF8(mode, u8Mode, 20) <= 0)
	{
		return nullptr;
	}

	return osFopen(u8FileName, u8Mode);
}

/**
 * @brief 打开指定文件
 * @param filename     文件名称（编码： UTF-32）
 * @param mode         打开模式（r w a b t +）
 * @return nullptr     打开失败
 * @return !nullptr    文件句柄
 * @note 当文件处于 assets 目录中时，cFileName 应该以 assets: 前缀开始
 *       例如： asset:/log/log.log
 * @note 当文件处于安卓系统下的assets目录中时，只读，其它权限将被忽略
 */
void *osFopen(const char32_t *filename, const char32_t *mode)
{
	if (nullptr == filename || nullptr == mode)
	{
		return nullptr;
	}

	char u8FileName[256] = {0x00};
	if (osConvertToUTF8(filename, u8FileName, 256) <= 0)
	{
		return nullptr;
	}
	char u8Mode[20] = {0x00};
	if (osConvertToUTF8(mode, u8Mode, 20) <= 0)
	{
		return nullptr;
	}

	return osFopen(u8FileName, u8Mode);
}

/**
 * @brief 读取指定文件
 * @param buf            存储读取数据的空间
 * @param size           每个元素大小，以字节为单位
 * @param count          元素个数
 * @param filehandle     文件句柄
 * @return >0            实际读取的元素个数
 * @return <=0           读取失败
 */
int32_t osFread(char *buf, const int32_t size, const int32_t count, void *filehandle)
{
	if (nullptr == buf || size <= 0 || count <= 0 || nullptr == filehandle)
	{
		return OSE_INVALID_PARAM;
	}

	OsFileHandle *pFileHandle = (OsFileHandle *)filehandle;
	if (nullptr == pFileHandle->m_pFileHandle)
	{
		return OSE_INVALID_PARAM;
	}

	if (pFileHandle->m_bAssets)
	{
		return AAsset_read((AAsset *)pFileHandle->m_pFileHandle, (void *)buf, size * count);
	}
	else
	{
		int32_t readbytes = (int32_t)fread((void *)buf, size, count, (FILE *)pFileHandle->m_pFileHandle);
		if (readbytes == count)
		{
			return readbytes;
		}

		if (ferror((FILE *)pFileHandle->m_pFileHandle))
		{
			return OSE_FAIL;
		}
		else
		{
			return readbytes;
		}
	}
}

/**
 * @brief 将缓冲区中内容，写入文件中
 * @param buf              缓冲区
 * @param size             每个缓冲区元素尺寸
 * @param count            缓冲区中存储元素个数
 * @param filehandle       文件句柄
 * @return >0              实际写入文件元素个数
 * @return <=0             写入失败
 * @note 如果文件位于安卓assets目录下，则不支持此操作，assets目录只读
 */
int32_t osFwrite(const char *buf, const int32_t size, const int32_t count, void *filehandle)
{
	if (nullptr == buf || size <= 0 || count <= 0 || nullptr == filehandle)
	{
		return OSE_INVALID_PARAM;
	}

	OsFileHandle *pFileHandle = (OsFileHandle *)filehandle;
	if (nullptr == pFileHandle->m_pFileHandle)
	{
		return OSE_INVALID_PARAM;
	}

	if (pFileHandle->m_bAssets)
	{ // assert 目录不支持写操作
		return OSE_FAIL;
	}
	else
	{
		const int32_t writebytes = (int32_t)fwrite((void *)buf, size, count, (FILE *)pFileHandle->m_pFileHandle);
		if (writebytes == count)
		{
			return writebytes;
		}

		if (ferror((FILE *)pFileHandle->m_pFileHandle))
		{
			return OSE_FAIL;
		}
		else
		{
			return writebytes;
		}
	}
}

/**
 * @brief 冲洗流中的信息，强迫将缓冲区内的数据
 *        写回参数filehandle指定的文件中
 * @param filehandle     文件句柄
 * @return  0    操作成功
 * @return !0    操作失败
 * @note 如果文件位于安卓assets目录下，则不支持此操作，assets目录只读
 */
int32_t osFflush(void *filehandle)
{
	if (nullptr == filehandle)
	{
		return -1;
	}

	OsFileHandle *pFileHandle = (OsFileHandle *)filehandle;
	if (nullptr == pFileHandle->m_pFileHandle)
	{
		return -2;
	}

	if (pFileHandle->m_bAssets)
	{ // assert 目录不支持此操作
		return -3;
	}

	return fflush((FILE *)pFileHandle->m_pFileHandle);
}

/**
 * @brief 设置流filehandle的文件位置为给定的偏移offset，
 *        参数offset意味着从给定的seekmode位置查找的字
 *        节数。
 * @param filehandle     文件句柄
 * @param offset         这是相对seekmode的偏移量，以字节为单位。
 * @param seekmode       这是表示开始添加偏移 offset 的位置。
 *                       SEEK_SET=0	文件的开头
 *                       SEEK_CUR=1	文件指针的当前位置
 *                       SEEK_END=2	文件的末尾
 * @return 0             成功
 * @return !0            失败
 * @note iOS系统目前只支持SEEK_SET的偏移，seekmode将被忽略
 * @note iOS 13.0 以上版本才支持此操作
 */
int32_t osFseek(void *filehandle, const int64_t offset, const int32_t seekmode)
{
	if (nullptr == filehandle || seekmode < 0 || seekmode >= 3)
	{
		return OSE_INVALID_PARAM;
	}

	OsFileHandle *pFileHandle = (OsFileHandle *)filehandle;
	if (nullptr == pFileHandle->m_pFileHandle)
	{
		return OSE_INVALID_PARAM;
	}

	const int32_t modes[] = {SEEK_SET, SEEK_CUR, SEEK_END};

	if (pFileHandle->m_bAssets)
	{
		const int64_t rt = AAsset_seek((AAsset *)pFileHandle->m_pFileHandle, offset, modes[seekmode]);
		if (-1 == rt)
		{
			return OSE_FAIL;
		}
		else
		{
			return OSE_OK;
		}
	}
	else
	{
		const int32_t rt = fseek((FILE *)pFileHandle->m_pFileHandle, offset, modes[seekmode]);
		if (0 == rt)
		{
			return OSE_OK;
		}
		else
		{
			return OSE_FAIL;
		}
	}
}

/**
 * @brief 返回给定流filehandle的当前文件位置。
 * @param filehandle    文件句柄
 * @return -1           错误
 * @return !-1          返回位置标识符的当前位置值。
 * @note 如果文件位于安卓assets目录下，则不支持此操作。
 * @note iOS 13.0 以上版本才支持此操作
 */
int64_t osFtell(void *filehandle)
{
	if (nullptr == filehandle)
	{
		return OSE_FAIL;
	}

	OsFileHandle *pFileHandle = (OsFileHandle *)filehandle;
	if (nullptr == pFileHandle->m_pFileHandle)
	{
		return OSE_FAIL;
	}

	if (pFileHandle->m_bAssets)
	{
		return OSE_FAIL;
	}

	return ftell((FILE *)pFileHandle->m_pFileHandle);
}

/**
 * @brief 从指定的流filehandle读取一行，并把它存储在buf所指向的字符串内
 *        当读取 (n-1) 个字符时，或者读取到换行符时，或者到达文件末尾时，
 *        它会停止，具体视情况而定。
 * @param buf              这是指向一个字符数组的指针，该数组存储了要读取的字符串。
 * @param size             这是要读取的最大字符数（包括最后的空字符）。通常是使用
 *                         以buf传递的数组长度。
 * @param filehandle       这是指向 FILE 对象的指针，该 FILE 对象标识了要从中读
 *                         取字符的流。
 * @return nullptr         读取失败
 * @return !nullptr        该函数返回相同的buf参数
 * @note 如果文件位于安卓assets目录下，则不支持此操作。
 * @note iOS暂不支持此操作
 */
char *osFgets(char *buf, const int32_t size, void *filehandle)
{
	if (nullptr == buf || size <= 0 || nullptr == filehandle)
	{
		return nullptr;
	}

	OsFileHandle *pFileHandle = (OsFileHandle *)filehandle;
	if (nullptr == pFileHandle->m_pFileHandle)
	{
		return nullptr;
	}

	if (pFileHandle->m_bAssets)
	{
		return nullptr;
	}

	char *pBuf = fgets(buf, size, (FILE *)pFileHandle->m_pFileHandle);
	if (nullptr == pBuf)
	{
		return nullptr;
	}

	return pBuf;
}

/**
 * @brief 关闭流 filehandle。刷新所有的缓冲区。
 * @param filehandle       文件句柄
 * @return 0               操作成功
 * @return !0              操作失败
 */
int32_t osFclose(void *filehandle)
{
	if (nullptr == filehandle)
	{
		return OSE_FAIL;
	}

	OsFileHandle *pFileHandle = (OsFileHandle *)filehandle;
	if (nullptr == pFileHandle->m_pFileHandle)
	{
		return OSE_FAIL;
	}

	if (pFileHandle->m_bAssets)
	{
		AAsset_close((AAsset *)pFileHandle->m_pFileHandle);
		free(pFileHandle);
		return OSE_OK;
	}
	else
	{
		int32_t iRet = fclose((FILE *)pFileHandle->m_pFileHandle);
		if (0 == iRet)
		{
			free(pFileHandle);
		}
		return iRet;
	}
}

int64_t osFsize(void *filehandle)
{
	if (nullptr == filehandle)
	{
		return OSE_FAIL;
	}

	OsFileHandle *pFileHandle = (OsFileHandle *)filehandle;
	if (nullptr == pFileHandle->m_pFileHandle)
	{
		return OSE_FAIL;
	}

	if (pFileHandle->m_bAssets)
	{
		return AAsset_getLength64((AAsset *)pFileHandle->m_pFileHandle);
	}
	else
	{
		int64_t iCurOffset = ftell((FILE *)pFileHandle->m_pFileHandle);
		if (iCurOffset < 0)
		{
			return OSE_FAIL;
		}
		if (0 != fseek((FILE *)pFileHandle->m_pFileHandle, 0, SEEK_END))
		{
			return OSE_FAIL;
		}
		const int64_t iSize = ftell((FILE *)pFileHandle->m_pFileHandle);
		fseek((FILE *)pFileHandle->m_pFileHandle, iCurOffset, SEEK_SET);
		return iSize;
	}
}

#endif // ANDROID

//___________________________________________文件结束____________________________________________